package program.commands;

import be.kuleuven.cs.som.annotate.*;
import program.conditions.*;
import item.*;

/**
 * A class concerning the ifSelections: (if(condition)(command)(command)).
 *
 * @invar	The condition of each selection must be valid condition.
 *                      | canHaveAsCondition(getCondition())
 * @invar       The if-command of each selection must be a valid command.
 *                      | canHaveAsCommand(getFirstCommand())
 * @invar       The else-command of each selection must be valid command.
 *                      | canHaveAsCommand(getSecondCommand())
 *
 */
public class IfSelection extends Commands {
	private final Conditions conditions;
	private final Commands firstCommand;
	private final Commands secondCommand;

	/**
	 * Initialise this new selection with a line number, condition and two commands.
	 *
	 * @param	condition
	 *                      The condition for executing the if-part.
	 * @param	firstCommand
	 *                      The command to be executed if the condition evaluates to true.
	 * @param    secondCommand
	 *                      The command to be executed if the condition evaluates to false.
	 * @pre         The condition for this selection must be valid.
	 *                      | canHaveAsCondition(condition)
	 * @pre         The if-command for this selection must be valid.
	 *                      | canHaveAsCommand(firstCommand)
	 * @pre         The else-command for this selection must be valid.
	 *                      | canHaveAsCommand(secondCommand)
	 * @post        The condition of this selection is set to the given condition.
	 *                      | new.getCondition() == condition
	 * @post        The if-command of this selection is set to the given command.
	 *                      | new.getFirstCommand() == firstCommand
	 * @post        The else-command of this selection is set to the given command.
	 *                      | new.getSecondCommand() == secondCommand
	 */
	public IfSelection(int lineNb, Conditions conditions, Commands firstCommand, Commands secondCommand) {
		super(lineNb);
		assert(canHaveAsConditions(conditions));
		assert(canHaveAsCommands(firstCommand));
		assert(canHaveAsCommands(secondCommand));
		this.conditions = conditions;
		this.firstCommand = firstCommand;
		this.secondCommand = secondCommand;
	}
	
	/**
	 * Returns the conditions associated with this selection.
	 */
	@Basic @Raw @Immutable
	public Conditions getConditions() {
		return conditions;
	}
	
	/**
	 * Returns the command to be executed if the condition evaluates to true.
	 */
	@Basic @Raw @Immutable
	public Commands getFirstCommand() {
		return firstCommand;
	}
	
	/**
	 * Returns the command to be executed if the condition evaluates to false.
	 */
	@Basic @Raw @Immutable
	public Commands getSecondCommand() {
		return secondCommand;
	}
	
	/**
	 * Checks whether the given condition is a valid one for a selection.
	 *
	 * @param	conditions
	 *               The conditions to check for whether they're valid.
	 * @return      
	 *              | result == ( condition != null )
	 */
	@Raw
	public boolean canHaveAsConditions(Conditions conditions) {
		return ( conditions != null);
	}
	
	/**
	 * Check whether the given commands are valid ones for a selection.
	 *
	 * @param	commands
	 *                The command to check for whether it is a valid one.
	 * @return         
	 *                | result == ( commands != null )
	 */
	@Raw
	public boolean canHaveAsCommands(Commands commands) {
		return (commands != null);
	}
	
	/**
	 * Return a textual representation of this selection,
	 * formatted in accordance with its position in the program.
	 *
	 * @return	The result is a pretty formatted string of this selection,
	 *                      taking in account his relative position in the program.
	 *                      | result ==
	 *                      |       getIndentation(indentationLevel) + "(if\n" +
	 *                      |       getCondition().prettyFormat(indentationLevel+1) + "\n" +
	 *                      |       getFirstCommand().prettyFormat(indentationLevel+1) + "\n" +
	 *                      |       getSecondCommand().prettyFormat(indentationLevel+1) + "\n" +
	 *                      |       getIndentation(indentationLevel) + ")"
	 */
	@Override
	public String prettyFormat(int indentationLevel) {
		return getIndentation(indentationLevel) + "(if\n" +
				getConditions().prettyFormat(indentationLevel+1) + "\n" +
				getFirstCommand().prettyFormat(indentationLevel+1) + "\n" +
				getSecondCommand().prettyFormat(indentationLevel+1) + "\n" +
				getIndentation(indentationLevel) + ")";
	}
	
	/**
	 * Return the next basic command for a robot, given the last executed line number.
	 *
	 * @return	
	 *          | if( lineNb >= getLineNb() )
	 *          |       the result == null
	 * @return	
	 *          | if(getCondition().evaluate(robot))
	 *          |       then result == getFirstCommand().getNextBasicCommand(lineNb, robot)
	 *          | else
	 *          |       then result == getSecondCommand().getNextBasicCommand(lineNb, robot);
	 */
	@Override
	public BasicCommand getNextBasicCommand(int lineNb, Robot robot) {
		if(lineNb >= getLineNb())	return null;
		if(getConditions().evaluate(robot))		return getFirstCommand().getNextBasicCommand(lineNb, robot);
		return getSecondCommand().getNextBasicCommand(lineNb, robot);
	}	
}

